//
// Created by user on 20-3-14.
//

#include "Trap.h"
#include "../ProcessManagement/Task.h"

// 初始化系统异常向量
void sysVectorInit() {

    set_trap_gate(0, 0, divide_error);
    set_trap_gate(1, 0, debug);
    set_intr_gate(2, 0, nmi);
    set_system_gate(3, 0, int3);
    set_system_gate(4, 0, overflow);
    set_system_gate(5, 0, bounds);
    set_trap_gate(6, 0, invalid_opcode);
    set_trap_gate(7, 0, device_not_available);
    set_trap_gate(8, 0, double_fault);
    set_trap_gate(9, 0, coprocessor_segment_overrun);
    set_trap_gate(10, 0, invalid_TSS);
    set_trap_gate(11, 0, segment_not_present);
    set_trap_gate(12, 0, stack_segment_fault);
    set_trap_gate(13, 0, general_protection);
    set_trap_gate(14, 0, page_fault);
    //15 Intel reserved. Do not use.
    set_reserve_gate(15, 0, 0);
    set_trap_gate(16, 0, x87_FPU_error);
    set_trap_gate(17, 0, alignment_check);
    set_trap_gate(18, 0, machine_check);
    set_trap_gate(19, 0, SIMD_exception);
    set_trap_gate(20, 0, virtualization_exception);

    for (int i = 21; i < 32; ++i) {
        set_reserve_gate(i, 0, 0);
    }


    //set_system_gate(SYSTEM_CALL_VECTOR,7,system_call);

}


int lookupKallsyms(unsigned long address, int level) {
    int index = 0;
    int level_index = 0;
    char *string = (char *) &kallsyms_names;
    for (index = 0; index < kallsyms_syms_num; index++)
        if (address > kallsyms_addresses[index] && address <= kallsyms_addresses[index + 1])
            break;
    if (index < kallsyms_syms_num) {
        for (level_index = 0; level_index < level; level_index++)
            printfColor(RED, BLACK, "  ");
        printfColor(RED, BLACK, "+---> ");

        printfColor(RED, BLACK, "address: %lx \t(+) %d function: %s\n", address,
                    address - kallsyms_addresses[index], &string[kallsyms_index[index]]);
        return 0;
    } else
        return 1;
}

void backtrace(PerformSite *regs) {
    unsigned long *rbp = (unsigned long *) regs->rbp;
    unsigned long ret_address = regs->rip;
    int i = 0;

    printfColor(RED, BLACK, "====================== Kernel Stack Backtrace ======================\n");

    for (i = 0; i < 100; i++) {
        if (lookupKallsyms(ret_address, i))
            break;
        if ((unsigned long) rbp < (unsigned long) regs->rsp || (unsigned long) rbp > current->thread->rsp0)
            break;

        ret_address = *(rbp + 1);
        rbp = (unsigned long *) *rbp;
    }
}

void displayRegs(PerformSite *regs) {
    printfColor(RED, BLACK, "CS: %lx, SS: %lx\nDS: %lx, ES: %lx\nRFLAGS: %lx\n", regs->cs, regs->ss, regs->ds,
                regs->es, regs->rflags);
    printfColor(RED, BLACK,
                "RAX: %lx, RBX: %lx, RCX: %lx, RDX: %lx\nRSP: %lx, RBP: %lx, RIP: %lx\nRSI: %lx, RDI: %lx\n",
                regs->rax, regs->rbx, regs->rcx, regs->rdx, regs->rsp, regs->rbp, regs->rip, regs->rsi, regs->rdi);
    printfColor(RED, BLACK,
                "R8 : %lx ,R9 : %lx\nR10: %lx, R11: %lx\nR12: %lx, R13: %lx\nR14: %lx, R15: %lx\n",
                regs->r8, regs->r9, regs->r10, regs->r11, regs->r12, regs->r13, regs->r14, regs->r15);
    backtrace(regs);
}


void do_divide_error(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_divide_error(0), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_debug(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_debug(1),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_nmi(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_nmi(2),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_int3(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_int3(3),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_overflow(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_overflow(4),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_bounds(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_bounds(5),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_invalid_opcode(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_undefined_opcode(6),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_device_not_available(PerformSite *regs, unsigned long error_code) { ;
    printfColor(RED, BLACK, "do_dev_not_available(7),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp,
                regs->rip);
    displayRegs(regs);
    while (1);
}

void do_double_fault(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_double_fault(8), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp, regs->rip);
//    displayRegs(regs);
//    backtrace(regs);
//    while (1);
}

void do_coprocessor_segment_overrun(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_coprocessor_segment_overrun(9),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n",
                error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_invalid_TSS(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_invalid_TSS(10),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x01)
        printfColor(RED, BLACK,
                    "The exception occurred during delivery of an event external to the program,such as an interrupt or an earlier exception.\n");

    if (error_code & 0x02)
        printfColor(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    else
        printfColor(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");

    if ((error_code & 0x02) == 0)
        if (error_code & 0x04)
            printfColor(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        else
            printfColor(RED, BLACK, "Refers to a descriptor in the current GDT;\n");

    printfColor(RED, BLACK, "Segment Selector Index:%lx\n", error_code & 0xfff8);

    displayRegs(regs);
    while (1);
}

void do_segment_not_present(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_segment_not_present(11),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp,
                regs->rip);

    if (error_code & 0x01)
        printfColor(RED, BLACK,
                    "The exception occurred during delivery of an event external to the program,such as an interrupt or an earlier exception.\n");

    if (error_code & 0x02)
        printfColor(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    else
        printfColor(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");

    if ((error_code & 0x02) == 0)
        if (error_code & 0x04)
            printfColor(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        else
            printfColor(RED, BLACK, "Refers to a descriptor in the current GDT;\n");

    printfColor(RED, BLACK, "Segment Selector Index: %lx\n", error_code & 0xfff8);

    displayRegs(regs);
    while (1);
}

void do_stack_segment_fault(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_stack_segment_fault(12),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp,
                regs->rip);

    if (error_code & 0x01)
        printfColor(RED, BLACK,
                    "The exception occurred during delivery of an event external to the program,such as an interrupt or an earlier exception.\n");

    if (error_code & 0x02)
        printfColor(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    else
        printfColor(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");

    if ((error_code & 0x02) == 0)
        if (error_code & 0x04)
            printfColor(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        else
            printfColor(RED, BLACK, "Refers to a descriptor in the current GDT;\n");

    printfColor(RED, BLACK, "Segment Selector Index: %lx\n", error_code & 0xfff8);

    displayRegs(regs);
    while (1);
}

void do_general_protection(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_general_protection(13), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp,
                regs->rip);

    if (error_code & 0x01)
        printfColor(RED, BLACK,
                    "The exception occurred during delivery of an event external to the program,such as an interrupt or an earlier exception.\n");

    if (error_code & 0x02)
        printfColor(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    else
        printfColor(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");

    if ((error_code & 0x02) == 0)
        if (error_code & 0x04)
            printfColor(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        else
            printfColor(RED, BLACK, "Refers to a descriptor in the current GDT;\n");

    printfColor(RED, BLACK, "Segment Selector Index: %lx\n", error_code & 0xfff8);

    displayRegs(regs);
    while (1);
}

void do_page_fault(PerformSite *regs, unsigned long error_code) {
    unsigned long cr2 = 0;

    __asm__ __volatile__("movq	%%cr2,	%0":"=r"(cr2)::"memory");

    printfColor(RED, BLACK, "do_page_fault(14), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp, regs->rip);

    if (!(error_code & 0x01))
        printfColor(RED, BLACK, "Page Not-Present,\t");

    if (error_code & 0x02)
        printfColor(RED, BLACK, "Write Cause Fault,\t");
    else
        printfColor(RED, BLACK, "Read Cause Fault,\t");

    if (error_code & 0x04)
        printfColor(RED, BLACK, "Fault in user(3)\t");
    else
        printfColor(RED, BLACK, "Fault in supervisor(0, 1, 2)\t");

    if (error_code & 0x08)
        printfColor(RED, BLACK, ",Reserved Bit Cause Fault\t");

    if (error_code & 0x10)
        printfColor(RED, BLACK, ",Instruction fetch Cause Fault");

    printfColor(RED, BLACK, "\n");

    printfColor(RED, BLACK, "CR2: %lx\n", cr2);

    displayRegs(regs);
    while (1);
}

void do_x87_FPU_error(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_x87_FPU_error(16), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_alignment_check(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_alignment_check(17), ERROR_CODE: %lx, RSP: %lx, RIP: %lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_machine_check(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_machine_check(18),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_SIMD_exception(PerformSite *regs, unsigned long error_code) {
    printfColor(RED, BLACK, "do_SIMD_exception(19),ERROR_CODE:%lx,RSP:%lx,RIP:%lx\n", error_code, regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}

void do_virtualization_exception(PerformSite *regs, unsigned long error_code) { ;
    printfColor(RED, BLACK, "do_virtualization_exception(20), ERROR_CODE: %lx,RSP: %lx,RIP: %lx\n", error_code,
                regs->rsp, regs->rip);
    displayRegs(regs);
    while (1);
}